package dou.helper;

import android.app.Activity;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import dou.utils.DLog;
import dou.utils.DisplayUtil;
import dou.utils.ToastUtil;

import static android.hardware.Camera.CameraInfo;
import static android.hardware.Camera.PreviewCallback;
import static android.hardware.Camera.getCameraInfo;
import static android.hardware.Camera.open;

@SuppressWarnings("deprecation")
public class CameraHelper implements PreviewCallback {
    private Camera camera = null;

    private SurfaceHolder surfaceHolder = null;
    private SurfaceView surfaceView;
    private SurfaceTexture mSurfaceTexture;
    private PreviewFrameListener previewFrameListener;
    private Context context;
    private Camera.Size previewSize;

    private int cameraFacing = CameraInfo.CAMERA_FACING_FRONT;
    private int sw, sh;
    private int camera_max_width = 0;
    private int camera_max_height = 0;
    private int rotate = 0;
    private int rotate_front = 0;
    private int pre_rate = 0;


    public CameraHelper(Context context, CameraParams params) {
        this.context = context;
        assert params != null;
        this.surfaceView = params.surfaceView;
        this.camera_max_width = params.preview_width;
        this.camera_max_height = params.preview_height;
        this.previewFrameListener = params.previewFrameListener;
        this.cameraFacing = params.firstCameraId;
        this.rotate = params.camera_ori;
        this.rotate_front = params.camera_ori_front;
        this.pre_rate = params.pre_rate;
        this.sw = DisplayUtil.getScreenWidthPixels(context);
        this.sh = DisplayUtil.getScreenHeightPixels(context);

        if (surfaceView != null) {
            surfaceHolder = surfaceView.getHolder();
            surfaceHolder.addCallback(new SurfaceHolder.Callback() {
                @Override
                public void surfaceCreated(SurfaceHolder holder) {
                    DLog.d("surfaceCreated****");
                    initCamera();
                }

                @Override
                public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
                    DLog.d("surfaceChanged****");
                    openCamera();
                }

                @Override
                public void surfaceDestroyed(SurfaceHolder holder) {
                    DLog.d("surfaceDestroyed****");
                    stopCamera();
                }
            });

            surfaceView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (camera != null)
                        camera.autoFocus(new Camera.AutoFocusCallback() {
                            @Override
                            public void onAutoFocus(boolean success, Camera camera) {

                            }
                        });
                }
            });
        } else {
            initCamera();
            openCamera();
        }
    }

    private void initCamera() {
        try {
            camera = open(cameraFacing);
        } catch (Exception e) {
//            new ToastUtil(context).showSingletonToast("摄像头开启失败，正在尝试开启另一个摄像头");
            //某些设备摄像头无法开启,使用open(back),设置当前摄像头为后置模式
            cameraFacing = (cameraFacing == CameraInfo.CAMERA_FACING_FRONT ?
                    CameraInfo.CAMERA_FACING_BACK : CameraInfo.CAMERA_FACING_FRONT);
            if (camera == null) {
                camera = open(cameraFacing);
            }
        }
        try {
            if (surfaceView != null)
                camera.setPreviewDisplay(surfaceHolder);
            else {
                if (mSurfaceTexture == null) {
                    mSurfaceTexture = new SurfaceTexture(-1);
                }
                camera.setPreviewTexture(mSurfaceTexture);
            }
        } catch (IOException e) {
            e.printStackTrace();
            new ToastUtil(context).showSingletonToast("error: 摄像头开启失败");
            if (null != camera) {
                camera.release();
                camera = null;
            }
        }
    }

    private void openCamera() {
        if (null != camera) {
            try {
                Camera.Parameters parameters = camera.getParameters();

                if (pre_rate != 0 && parameters.isZoomSupported()) {
                    pre_rate = pre_rate < 0 ? 0 : pre_rate;
                    pre_rate = pre_rate > 100 ? 100 : pre_rate;
                    int max = parameters.getMaxZoom();
                    parameters.setZoom(max * pre_rate / 100);
                }

                int oritation_result = setCameraDisplayOrientation((Activity) context, getCameraId(), camera);

                setOptimalPreviewSize(parameters, camera_max_width, oritation_result);
                camera.setParameters(parameters);
                startPreview();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public void setZoom(int pre) {
        if (camera == null) return;
        pre = pre < 0 ? 0 : pre;
        pre = pre > 100 ? 100 : pre;
        Camera.Parameters parameters = camera.getParameters();
        int max = parameters.getMaxZoom();
        parameters.setZoom(max * pre / 100);
        camera.setParameters(parameters);
    }

    private void setOptimalPreviewSize(Camera.Parameters cameraParams,
                                       int targetWidth, int oritation_result) {
        List<Camera.Size> supportedPreviewSizes = cameraParams.getSupportedPreviewSizes();

        if (null == supportedPreviewSizes) {
        } else {
            Camera.Size optimalSize = null;
            if (targetWidth == -1) {
                //大于640,且跟屏幕分辨率最接近的分辨率
                double minDiff = 1.7976931348623157E308D;
                Iterator mIterator = supportedPreviewSizes.iterator();
                while (mIterator.hasNext()) {
                    Camera.Size size = (Camera.Size) mIterator.next();
                    if ((double) Math.abs(size.width - 1000) <= minDiff) {
                        minDiff = (double) Math.abs(size.width - 1000);

                        if (optimalSize != null && ((sh < sw && optimalSize.width * sh == optimalSize.height * sw)
                                || (sh > sw && optimalSize.width * sw == optimalSize.height * sh))) {
                        } else
                            optimalSize = size;
                    }
                }
            }

            if (optimalSize == null) {
                targetWidth = targetWidth == -1 ? 640 : targetWidth;
                double minDiff = 1.7976931348623157E308D;
                Iterator mIterator = supportedPreviewSizes.iterator();
                while (mIterator.hasNext()) {

                    Camera.Size size = (Camera.Size) mIterator.next();
                    if (size.width == camera_max_width && size.height == camera_max_height) {
                        optimalSize = size;
                        break;
                    }
                    if ((double) Math.abs(size.width - targetWidth) <= minDiff) {
                        minDiff = (double) Math.abs(size.width - targetWidth);


//                        if (optimalSize != null && ((sh < sw && optimalSize.width * sh == optimalSize.height * sw)
//                                || (sh > sw && optimalSize.width * sw == optimalSize.height * sh))) {
//                        } else
                        optimalSize = size;
                    }
                }
            }
            setPreviewSize(optimalSize);
            int preview_width = optimalSize.width;
            int preview_height = optimalSize.height;


            if (surfaceView != null) {
                //640 480 1920 1080
                int surface_width = 0;
                int surface_height = 0;

                if (oritation_result % 180 == 0) {//横屏preview_width->sw
                    if (sw * preview_height / preview_width <= sh) {
                        surface_width = sw;
                        surface_height = sw * preview_height / preview_width;
                    } else if (sh * preview_width / preview_height <= sw) {//preview_height->sh
                        surface_height = sh;
                        surface_width = sh * preview_width / preview_height;
                    }
                } else {//preview_width->sh
                    if (sh * preview_height / preview_width <= sw) {
                        surface_height = sh;
                        surface_width = sh * preview_height / preview_width;
                    } else if (sw * preview_width / preview_height <= sh) {//preview_height->sw
                        surface_width = sw;
                        surface_height = sw * preview_width / preview_height;
                    }
                }

                // FIXME puyz add start
                surface_width = sw;
                surface_height = sw * preview_height / preview_width;
                // FIXME puyz add end

                surfaceView.getLayoutParams().width = surface_width;
                surfaceView.getLayoutParams().height = surface_height;

                surfaceView.requestLayout();
                Log.d("DLog", surface_width + ":" + surface_height + ":" + sw + ":" + sh);
            }

            cameraParams.setPreviewSize(preview_width, preview_height);
        }
    }


    public Camera.Size getPreviewSize() {
        return previewSize;
    }

    public void stopCamera() {
        if (null != camera) {

            if (mSurfaceTexture != null) {
                mSurfaceTexture.release();
                mSurfaceTexture = null;
            }

            camera.setPreviewCallbackWithBuffer(null);
            camera.stopPreview();
            camera.release();
            camera = null;
        }
    }

    private void setPreviewSize(Camera.Size previewSize) {
        this.previewSize = previewSize;
    }

    public int switchCameraId() {
        cameraFacing = (cameraFacing == CameraInfo.CAMERA_FACING_FRONT ?
                CameraInfo.CAMERA_FACING_BACK : CameraInfo.CAMERA_FACING_FRONT);
        if (!hasFacing(cameraFacing)) return cameraFacing;
        stopCamera();
        initCamera();
        openCamera();
        return cameraFacing;
    }

    public int getCameraId() {
        return cameraFacing;
    }

    public void startPreview() {
        camera.startPreview();
        camera.setPreviewCallbackWithBuffer(this);
        camera.addCallbackBuffer(new byte[((previewSize.width * previewSize.height) * ImageFormat.getBitsPerPixel(ImageFormat.NV21)) / 8]);
    }

    public void stopPreview() {
        if (camera != null)
            camera.stopPreview();
    }

    public int switchCamera() {
        return switchCameraId();
    }


    public interface PreviewFrameListener {
        void onPreviewFrame(byte[] data, Camera camera);
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        camera.addCallbackBuffer(data);
        if (previewFrameListener != null)
            previewFrameListener.onPreviewFrame(data, camera);
    }


    private boolean hasFacing(int facing) {
        CameraInfo info = new CameraInfo();
        for (int i = 0; i < Camera.getNumberOfCameras(); i++) {
            getCameraInfo(i, info);

            if (info.facing == facing) {
                return true;
            }
        }
        return false;
    }

    private int setCameraDisplayOrientation(Activity activity,
                                            int cameraId, android.hardware.Camera camera) {

        android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
        android.hardware.Camera.getCameraInfo(cameraId, info);
        int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
        int degrees = 0;
        switch (rotation) {
            case Surface.ROTATION_0:
                degrees = 0;
                break;
            case Surface.ROTATION_90:
                degrees = 90;
                break;
            case Surface.ROTATION_180:
                degrees = 180;
                break;
            case Surface.ROTATION_270:
                degrees = 270;
                break;
        }

        int result;
        if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
            result = (info.orientation + degrees) % 360;
            result = (360 - result) % 360;  // compensate the mirror
            if (rotate_front != -1) result = rotate_front;
        } else {  // back-facing
            result = (info.orientation - degrees + 360) % 360;
            if (rotate != -1) result = rotate;
        }

        DLog.d(rotation + " : " + info.orientation + " : " + result);
        camera.setDisplayOrientation(result);
        return result;
    }

    private MediaRecorder mediaRecorder;

    public void startRecord(String name) throws IOException {
        camera.lock();
        camera.unlock();
        // Step 1:
        mediaRecorder = new MediaRecorder();
        mediaRecorder.setCamera(camera);
//            mediaRecorder.setOrientationHint(0);

        // Step 2: Set sources
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

        //构造CamcorderProfile，使用高质量视频录制
        mediaRecorder.setProfile(CamcorderProfile.get(cameraFacing, CamcorderProfile.QUALITY_HIGH));

        mediaRecorder.setVideoSize(640, 480);

        // Step 4: Set output file
        mediaRecorder.setOutputFile(name);
        mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());

        mediaRecorder.prepare();

        mediaRecorder.start();

    }

    public Camera getCamera() {
        return camera;
    }

    public SurfaceHolder getSurfaceHolder() {
        return surfaceHolder;
    }


    public void stopRecord() {
        if (mediaRecorder != null) {
            mediaRecorder.reset();
            camera.lock();
            mediaRecorder.release();
            mediaRecorder = null;
        }
    }
}
